package com.jianqiao.delivery.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSAEncrypter;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.IOUtils;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Enumeration;


/**
 * author: cyx
 * create: 2024/11/25 16:12
 **/
public class EncryptionUtils {
    private static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
    private static final String END_CERT = "-----END CERTIFICATE-----";
    private static final String BEGIN_RSA_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----";
    private static final String END_RSA_PRIVATE_KEY = "-----END RSA PRIVATE KEY-----";
    private static final String ENC_DATA = "encData";

//    public static void main(String[] args) throws CertificateException, IOException, JOSEException {
//        List<NationalIdentifiersEntity> nationalIdentifiersList = new ArrayList<>();
//        NationalIdentifiersEntity nationalIdentifiersEntity = NationalIdentifiersEntity.builder().type("PASSPORT").value("LB44889298222").build();
//        nationalIdentifiersList.add(nationalIdentifiersEntity);
//        CreditorEntity creditorEntity = CreditorEntity.builder()
//                .creditorAgentCountry("UA")
//                .creditorAgentId("VD123445")
//                .creditorCountry("UA")
//                .nationalIdentifiers(nationalIdentifiersList).build();
//
//        PaymentRequestArrayEntity paymentRequestArrayEntity = PaymentRequestArrayEntity.builder().debtorAgentCountry("UA")
//                .debtorCountry("UA")
//                .endToEndId("RFPid0001")
//                .requestedAmount(100)
//                .requestedAmountCurrency("UAH").build();
//        List<PaymentRequestArrayEntity> paymentRequestArrayList = new ArrayList<>();
//        paymentRequestArrayList.add(paymentRequestArrayEntity);
//        PaymentRequestEntity paymentRequestEntity = PaymentRequestEntity.builder()
//                .paymentRequestList(paymentRequestArrayList).build();
//
//        SettlementOptionsArrayEntity settlementOptionsArrayEntity = SettlementOptionsArrayEntity.builder().settlementSystem("VISA_DIRECT").build();
//        List<SettlementOptionsArrayEntity> settlementOptionsArrayList = new ArrayList<>();
//        settlementOptionsArrayList.add(settlementOptionsArrayEntity);
//        SettlementOptionsEntity settlementOptionsEntity = SettlementOptionsEntity.builder()
//                .settlementOptionsList(settlementOptionsArrayList).build();
//
//        InitiateR2PEntity initiateR2PEntity = InitiateR2PEntity.builder()
//                .creationDateTime("2020-12-17T09:30:47Z")
//                .creditor(creditorEntity)
//                .paymentRequests(paymentRequestEntity)
//                .product("Test Product")
//                .requestMessageId("12345")
//                .settlementOptions(settlementOptionsEntity)
//                .useCase("P2P").build();
//        String paramJsonStr = JSONObject.toJSONString(initiateR2PEntity);
//        String encryptedPayload = getEncryptedPayload(paramJsonStr, "f20f9b7f-ba54-4ad0-8412-f979daf52dd5");
//        System.out.println(encryptedPayload);
//    }

    /**
      *methood: 加密请求负载报文
      *auth: cyx
      *time: 2024/11/26 11:29
    **/
    //Make sure you are reading and passing the correct keyId from credentials. This is required and is passed in headers.
    public static String getEncryptedPayload(Object payload, String keyId) throws CertificateException, JOSEException, IOException {

        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

        String plainText = payload == null ? "" : mapper.writeValueAsString(payload);
        JWEHeader.Builder headerBuilder = new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128GCM);

        headerBuilder.keyID(keyId);
        headerBuilder.customParam("iat", System.currentTimeMillis());

        JWEObject jweObject = new JWEObject(headerBuilder.build(), new Payload(plainText));
        jweObject.encrypt(new RSAEncrypter(getRSAPublicKey()));
        Base64URL encryptedKey = jweObject.getEncryptedKey();
        return "{\"encData\":\""+jweObject.serialize()+"\"}";
    }

    /**
      *methood: 获得加密公钥
      *auth: cyx
      *time: 2024/11/26 11:29
    **/
    /*
     * Converts PEM file content to RSAPublicKey
     */
    private static RSAPublicKey getRSAPublicKey() throws CertificateException, IOException {
        String mleServerPublicCertificatePath = "C:\\Users\\os\\Desktop\\VISA材料\\第三项目-MLE-默认\\server_cert_f20f9b7f-ba54-4ad0-8412-f979daf52dd5.pem";
        String pemEncodedPublicKey = IOUtils.readFileToString(new File(mleServerPublicCertificatePath), Charset.forName("UTF-8"));
        Base64 base64 = new Base64(
                pemEncodedPublicKey.replaceAll(BEGIN_CERT, "").replaceAll(END_CERT, ""));
        Certificate cf = CertificateFactory.getInstance("X.509")
                .generateCertificate(new ByteArrayInputStream(base64.decode()));
        return (RSAPublicKey) cf.getPublicKey();
    }

    /**
      *methood: 解密响应报文
      *auth: cyx
      *time: 2024/11/26 11:28
    **/
    public static <T> T getDecryptedPayload(String response, Class<T> returnType)  {
//        String response = encryptedPayload.getEncData();
        T decryptedResponse = null;
        try {
            JWEObject jweObject = JWEObject.parse(response);
            //If you have used passphrase while generating the csr make sure you the same while getting the private key. Otherwise decryption will fail.
            jweObject.decrypt(new RSADecrypter(getRSAPrivateKey()));
            response = jweObject.getPayload().toString();
            ObjectMapper mapper = new ObjectMapper();
            decryptedResponse = mapper.readValue(response, returnType);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return decryptedResponse;
    }

    /**
      *methood: 获得解密私钥
      *auth: cyx
      *time: 2024/11/26 11:29
    **/
    /*
     * Converts PEM file content to RSAPrivateKey
     */
    private static PrivateKey getRSAPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        String mleClientPrivateKeyPath = "C:\\Users\\os\\Desktop\\VISA材料\\第三项目-MLE-默认\\key_f20f9b7f-ba54-4ad0-8412-f979daf52dd5.pem";

        //If you have used passphrase while generating the csr make sure you the same while reading the private key. Otherwise decryption will fail.
        String pemEncodedKey = IOUtils.readFileToString(new File(mleClientPrivateKeyPath), Charset.forName("UTF-8"));
        Base64 base64 = new Base64(pemEncodedKey.replaceAll(BEGIN_RSA_PRIVATE_KEY, "").replaceAll(END_RSA_PRIVATE_KEY, ""));
        ASN1Sequence primitive = (ASN1Sequence) ASN1Sequence.fromByteArray(base64.decode());
        Enumeration<?> e = primitive.getObjects();
        BigInteger v = ((ASN1Integer) e.nextElement()).getValue();
        int version = v.intValue();
        if (version != 0 && version != 1) {
            throw new IllegalArgumentException("wrong version for RSA private key");
        }
        BigInteger modulus = ((ASN1Integer) e.nextElement()).getValue();
        ((ASN1Integer) e.nextElement()).getValue();
        BigInteger privateExponent = ((ASN1Integer) e.nextElement()).getValue();
        ((ASN1Integer) e.nextElement()).getValue();
        ((ASN1Integer) e.nextElement()).getValue();
        ((ASN1Integer) e.nextElement()).getValue();
        ((ASN1Integer) e.nextElement()).getValue();
        ((ASN1Integer) e.nextElement()).getValue();
        RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(modulus, privateExponent);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return (PrivateKey) keyFactory.generatePrivate(privateKeySpec);
    }
}
